////////////////////////////////////////////////////////////////
///
/// File: BinaryTmpExp.m
/// Author: Lucio Davide Spano
/// Last update: 26 August 2012
/// Description: This file contains the definition of a binary
///              temporal expression
/// To do:
/// ------------------------------------------------------------
/// Copyright (c) 2012 Lucio Davide Spano
///                     (lucio.davide.spano@isti.cnr.it)
///
/// The use and distribution terms for this software are
/// contained in the file named license.txt, which can be found
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------
////////////////////////////////////////////////////////////////

#import "BinaryTmpExp.h"
#import "TmpExpProtected.h"

@class ExpEventArgs, Token;

@interface BinaryTmpExp (private)

- (void) applyPerforming:(TmpExp*) exp :(ExpEventArgs*) args :(Token*) token;
- (void) applyComplete:(TmpExp*) exp :(ExpEventArgs*) args :(Token*) token;
- (bool) isLeftChild:(TmpExp*) exp;
- (bool) isRightChild:(TmpExp*) exp;

@end

@implementation BinaryTmpExp (private)



- (void) applyPerforming:(TmpExp *)exp :(ExpEventArgs *)args :(Token *)_token{
    
    switch(self.tmpOperator){
        case ImmediateChoice:{
            if([self isLeftChild:exp]){
                // execute the left operand of a choice,
                // disables the right operand
                [self.rightOperand setExecState:Disabled :_token];
            }else if([self isRightChild:exp]){
                // execute the right operand of a choice,
                // disable the left operand
                [self.leftOperand setExecState:Disabled :_token];
            }
            [self setExecState:Performing :_token];
            [self raisePerforming:self :args :_token];
        }break;
            
        case Choice:{
            // no action in this case, we wait until one of the two
            // operands completes
            [self setExecState:Performing :_token];
            [self raisePerforming:self :args :_token];
            [self raiseOnStateChanged:self :args :_token];
        }break;
            
        case Parallel:
        case SynchronizedParallel:
            // nothing to do
            break;
            
        case Disabling:{
            if([self isRightChild:exp]){
                // starting the execution of the right operand
                // disables the left one
                [self.leftOperand setExecState:Disabled :_token];
            }
            
            [self setExecState:Performing :_token];
            [self raisePerforming:self :args :_token];
            [self raiseOnStateChanged:self :args :_token];
        }break;
            
        case Enabling:{
            if([self isLeftChild:exp]){
                // disable the right operand during the execution of the left one
                [self.rightOperand setExecState:Disabled :_token];
            }else if([self isRightChild:exp]){
                // disable the left operand, that should be already completed
                [self.leftOperand setExecState:Disabled :_token];
            }
            [self setExecState:Performing :_token];
            [self raisePerforming:self :args :_token];
            [self raiseOnStateChanged:self :args :_token];
        }break;
            
        default: break;
    }
}

- (void) applyComplete:(TmpExp *)exp :(ExpEventArgs *)args :(Token *)_token{
    switch(self.tmpOperator){
        case Choice:
        case ImmediateChoice:{
            // if one operand has been executed, the other one should be
            // disabled
            if([self isLeftChild:exp]){
                [self.rightOperand setExecState:Disabled :_token];
            }else if([self isRightChild:exp]){
                [self.leftOperand setExecState:Disabled :_token];
            }
            // set current execution state to complete
            [self setExecState:Completed :_token];
            [self raiseCompleted:self :args :_token];
            [self raiseOnStateChanged:self :args :_token];
        }break;
            
        case SynchronizedParallel:{
            if([self isLeftChild:exp] || self.leftOperand.optional || self.leftOperand.state == Completed){
                leftCompleted = true;
            }
            if([self isRightChild:exp] || self.rightOperand.optional || self.rightOperand.state == Completed){
                rightCompleted = true;
            }
            if(leftCompleted && rightCompleted){
                [self setExecState:Completed :_token];
                [self raiseCompleted:self :args :_token];
                [self raiseOnStateChanged:self :args :_token];
            }else{
                [self setExecState:Performing :_token];
                [self raisePerforming:self :args :_token];
                [self raiseOnStateChanged:self :args :_token];
            }
        }break;
            
        case Parallel:{
            // differentely from the synchronized parallel
            // we do not track the execution of the operands
            [self setExecState:Completed :_token];
            [self raiseCompleted:self :args :_token];
            [self raiseOnStateChanged:self :args :_token];
        }break;
            
        case Disabling:{
            if([self isRightChild:exp]){
                [self.leftOperand setExecState:Disabled :_token];
                [self setExecState:Completed :_token];
                [self raiseCompleted:self :args :_token];
                [self raiseOnStateChanged:self :args :_token];
            }else{
                [self setExecState:Performing :_token];
                [self raisePerforming:self :args :_token];
                [self raiseOnStateChanged:self :args :_token];
            }
        }break;
            
        case Enabling:{
            if([self isLeftChild:exp]){
                if(self.rightOperand.optional){
                    [self setExecState:Completed :_token];
                    [self.rightOperand setExecState:Completed :_token];
                    [self raiseCompleted:self :args :_token];
                    [self raiseOnStateChanged:self :args :_token];
                }else{
                    [self.rightOperand setExecState:Enabled :_token];
                    [self setExecState:Performing :_token];
                    [self raisePerforming:self :args :_token];
                    [self raiseOnStateChanged:self :args :_token];
                }
            }else if([self isRightChild:exp]){
                [self setExecState:Completed :_token];
                [self raiseCompleted:self :args :_token];
                [self raiseOnStateChanged:self :args :_token];
            }
        }break;
            
        default: break;
    }
    
    if(self.iterative && (self.state == Completed || self.state == Error)){
        [self setExecState:Enabled :_token];
    }
}

- (bool) isLeftChild:(TmpExp *)exp{
    if(self.leftOperand != nil && self.leftOperand == exp){
        return true;
    }else{
        return false;
    }
}

- (bool) isRightChild:(TmpExp*)exp{
    if(self.rightOperand != nil && self.rightOperand == exp){
        return true;
    }else{
        return false;
    }
}

@end


@implementation BinaryTmpExp

@synthesize leftOperand = _leftOperand;
@synthesize rightOperand = _rightOperand;


- (id) init{
    self = [super init];
    self.tmpOperator = None;
    _leftOperand = nil;
    _rightOperand = nil;
    self.type = Binary;
    return self;
}

- (id) initWithOperands:(TmpExp*) leftOperand :(TmpExp*) rightOperand :(int) tmpOp{
    self = [self init];
    if(self){
        _leftOperand = leftOperand;
        [_leftOperand addStateChangeListener:self];
        _leftOperand.parent = self;
        _rightOperand = rightOperand;
        [_rightOperand addStateChangeListener:self];
        _rightOperand.parent = self;
        self.tmpOperator = tmpOp;
    }
    return self;
}


- (void) setLeftOperand:(TmpExp *)leftOperand{
    _leftOperand = leftOperand;
    if(_leftOperand != nil){
        [_leftOperand addStateChangeListener:self];
        _leftOperand.parent = self;
    }
}

- (void) setRightOperand:(TmpExp *)rightOperand{
    _rightOperand = rightOperand;
    if(_rightOperand != nil){
        [_rightOperand addStateChangeListener:self];
        _rightOperand.parent = self;
    }
}


- (void) setExecState:(int)state :(Token *)_token{
    [super setExecState:state :_token];
    switch(state){
            
        case Disabled:{
            if(self.leftOperand != nil){
                [self.leftOperand setExecState:Disabled :_token];
            }
            if(self.rightOperand != nil){
                [self.rightOperand setExecState:Disabled :_token];
            }
        }break;
            
        case Enabled:{
            
            switch(self.tmpOperator){
                case SynchronizedParallel:{
                    leftCompleted = false;
                    rightCompleted = false;
                }
                case ImmediateChoice:
                case Choice:
                case Parallel:
                case Disabling:
                case OrderIndependence:{
                    if(self.leftOperand != nil){
                        [self.leftOperand setExecState:Enabled :_token];
                    }
                    if(self.rightOperand != nil){
                        [self.rightOperand setExecState:Enabled :_token];
                    }
                    
                }break;
                    
                case Enabling:{
                    if(self.leftOperand != nil){
                        [self.leftOperand setExecState:Enabled :_token];
                    }
                }break;
                    
                default: break;
                    
            }
        }break;
            
            
        default: break;
    }
}

- (void) onStateChanged:(TmpExp *)sender :(ExpEventArgs *)args :(Token *)_token{
    if(self.tmpOperator != None){
        switch(sender.state){
            case Error:{
                [self setExecState:Error :_token];
                [self raiseError:self :args :_token];
                [self raiseOnStateChanged:self :args :_token];
                if(self.iterative == true && _token != nil){
                    // avoid enabling null token
                    [self setExecState:Enabled :_token];
                }
                
            }break;
                
            case Performing:{
                [self applyPerforming:sender :args :_token];
            }break;
                
            case Completed:{
                [self applyComplete:sender :args :_token];
            }break;
                
            default: break;
        }
    }
}

- (void)onNotAcceptedToken:(TmpExp *)sender :(SimpleTmpExp *)originalSender :(ExpEventArgs *)args :(Token *)token{
    switch (self.tmpOperator) {
        case Choice:{
            if([self bothEnabled] == true){
                bool leftAccept = [self.leftOperand accepts:args];
                if(!leftAccept){
                    [self.leftOperand setExecState:Disabled :token];
                    [self.leftOperand raiseError:self.leftOperand :args :token];
                }
            }
            
            if([self bothEnabled] == true){
                bool rightAccept = [self.rightOperand accepts:args];
                if(!rightAccept){
                    [self.rightOperand setExecState:Disabled :token];
                    [self.rightOperand raiseError:self.rightOperand :args :token];
                }
            }
            
            
        } break;
            
        default:
            break;
    }
    [self raiseOnNotAcceptedToken:originalSender :args :token];
}

- (bool) bothEnabled{
    return self.leftOperand != nil && (self.leftOperand.state == Enabled || self.leftOperand.state == Performing) &&
    self.rightOperand != nil && (self.rightOperand.state == Enabled || self.rightOperand.state == Performing);
}


- (bool) accepts:(ExpEventArgs *)args{
    switch(self.tmpOperator){
        case ImmediateChoice:
        case Choice:
        case Parallel:
        case SynchronizedParallel:
        case Enabling:
        case Disabling:{
            if(self.leftOperand != nil && [self.leftOperand accepts:args]){
                return true;
            }
            if(self.rightOperand != nil && [self.rightOperand accepts:args]){
                return true;
            }
            return false;
            
        }break;
            
            
        case OrderIndependence:
        case None: return false;
            
    }
}

- (bool) canAccept:(TmpExp*) exp{
    switch(exp.state){
        case Performing:
        case Enabled:
        case Completed:
            return true;
        default:return false;
    }
}
@end







